package com.hn.springframework.webmvc.servlet;

import com.hn.springframework.annotation.HNController;
import com.hn.springframework.annotation.HNRequestMapping;
import com.hn.springframework.context.HNApplicationContext;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Auther: ninghu
 * @Description: Spring mvc 核心控制类
 */
@Slf4j
public class HNDispatcherServlet extends HttpServlet {

    private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";
    private final String TEMPLATE_ROOT = "templateRoot";

    private HNApplicationContext context;

    private List<HNHandlerMapping> handlerMappings = new ArrayList<HNHandlerMapping>();
    private Map<HNHandlerMapping, HNHandlerAdapter> handlerAdapters = new HashMap<HNHandlerMapping, HNHandlerAdapter>();
    private List<HNViewResolver> viewResolvers = new ArrayList<HNViewResolver>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        context = new HNApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
        initStrategies(context);
    }


    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        HNHandlerMapping handler = getHandler(req);
        if (null == handler) {
            processDispatchResult(req,resp,new HNModelAndView("404"));
            return;
        }
        HNHandlerAdapter ha = getHandlerAdapter(handler);
        HNModelAndView mv = ha.handle(req,resp,handler);
        processDispatchResult(req, resp, mv);
    }

    private HNHandlerMapping getHandler(HttpServletRequest req) {
        if (this.handlerMappings.isEmpty()) {
            return null;
        }
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        for (HNHandlerMapping handler : this.handlerMappings) {
            Matcher matcher = handler.getPattern().matcher(url);
            if (!matcher.matches()) { continue; }
            return handler;
        }
        return null;
    }

    private HNHandlerAdapter getHandlerAdapter(HNHandlerMapping handler) {
        if(this.handlerAdapters.isEmpty()){return null;}
        HNHandlerAdapter ha = this.handlerAdapters.get(handler);
        if (ha.supports(handler)) {
            return ha;
        }
        return null;
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, HNModelAndView mv) throws Exception {
        //把 ModleAndView 变成一个HTML、OuputStream、json、freemark、veolcity
        if(null == mv){return;}

        if(this.viewResolvers.isEmpty()){return;}

        for (HNViewResolver viewResolver : this.viewResolvers) {
            HNView view = viewResolver.resolveViewName(mv.getViewName(),null);
            view.render(mv.getModel(),req,resp);
            return;
        }
    }

    //初始化策略
    private void initStrategies(HNApplicationContext context) {
        //多文件上传的组件
//        initMultipartResolver(context);
        //初始化本地语言环境
//        initLocaleResolver(context);
        //初始化模板处理器
//        initThemeResolver(context);
        //handlerMapping
        initHandlerMappings(context);
        //初始化参数适配器
        initHandlerAdapters(context);
        //初始化异常拦截器
//        initHandlerExceptionResolvers(context);
        //初始化视图预处理器
//        initRequestToViewNameTranslator(context);
        //初始化视图转换器
        initViewResolvers(context);
        //参数缓存器
//        initFlashMapManager(context);
    }

    private void initHandlerMappings(HNApplicationContext context) {
        String[] beanNames = context.getBeanDefinitionNames();
        try {
            for (String beanName : beanNames) {
                Object controller = context.getBean(beanName);
                Class<?> clazz = controller.getClass();
                if (!clazz.isAnnotationPresent(HNController.class)) {
                    continue;
                }
                String baseUrl = "";
                // 获取 Controller 的url配置
                if (clazz.isAnnotationPresent(HNRequestMapping.class)) {
                    HNRequestMapping requestMapping = clazz.getAnnotation(HNRequestMapping.class);
                    baseUrl = requestMapping.value();
                }
                // 获取 Method 的 url 配置
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(HNRequestMapping.class)) { continue; }
                    HNRequestMapping requestMapping = method.getAnnotation(HNRequestMapping.class);
                    String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*", ".*")).replaceAll("/+","/");
                    Pattern pattern = Pattern.compile(regex);

                    this.handlerMappings.add(new HNHandlerMapping(controller, method, pattern));
                    log.info("Url Mapped " + regex + " " + method);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initHandlerAdapters(HNApplicationContext context) {
        // 把 request 请求变成一个 handler，参数是字符串，自动配到 handler 的形参中
        // TODO Spring mvc 中怎么初始化的还需要再研究
        for (HNHandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapters.put(handlerMapping, new HNHandlerAdapter());
        }
    }

    private void initViewResolvers(HNApplicationContext context) {
        // 模板根目录
        String templateRoot = context.getConfig().getProperty(TEMPLATE_ROOT);
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        File templateRootDir = new File(templateRootPath);
        String[] templates = templateRootDir.list();
        // TODO 错误
        for (String template : templates) {
            this.viewResolvers.add(new HNViewResolver(templateRoot));
        }
    }

}
